<template>
  <div ref="canvas"></div>
</template>

<script setup>
import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { ref, onMounted } from "vue";
import {
  initDefaultLighting,
  applyMeshNormalMaterial,
  redrawGeometryAndUpdateUI,
  applyMeshStandardMaterial
} from "@/book/ThreeUtils";

// 画布
const canvas = ref();
// 场景
const scene = new THREE.Scene();
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setClearColor(0x000000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
// 相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(-30, 40, 30);
camera.lookAt(0, 0, 0,);
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
// 性能统计
const state = Stats();
// plane
const planeGeometry = new THREE.PlaneGeometry(10000, 10000);
const planeTexture = new THREE.TextureLoader().load(new URL("@/assets/textures/general/floor-wood.jpg", import.meta.url).href);
const planeMaterial = new THREE.MeshPhongMaterial({
  color: 0xffffff,
  map: planeTexture,
});
planeMaterial.map.wrapS = THREE.RepeatWrapping;
planeMaterial.map.wrapT = THREE.RepeatWrapping;
planeMaterial.map.repeat.set(80,80);
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotateX(-0.5 * Math.PI);
plane.position.set(0, -10, 0);
scene.add(plane);
// light
initDefaultLighting(scene, undefined);
// gui
const gui = new GUI();
const controlObj = new function () {
  this.appliedMaterial = applyMeshNormalMaterial;
  this.castShadow = true;
  this.groundPlaneVisible = true;
  this.planeGeometry = new THREE.PlaneGeometry(20,20,4,4);
  this.width = this.planeGeometry.parameters.width;
  this.height = this.planeGeometry.parameters.height;
  this.widthSegments = this.planeGeometry.parameters.widthSegments;
  this.heightSegments = this.planeGeometry.parameters.heightSegments;
  this.redraw = () => {
    redrawGeometryAndUpdateUI(gui, scene, controlObj, () => {
      return new THREE.PlaneGeometry(
          controlObj.width,
          controlObj.height,
          Math.round(controlObj.widthSegments),
          Math.round(controlObj.heightSegments)
      );
    })
  }
};

gui.add(controlObj, 'width', 0, 40).onChange(controlObj.redraw);
gui.add(controlObj, 'height', 0, 40).onChange(controlObj.redraw);
gui.add(controlObj, 'widthSegments', 0, 10).onChange(controlObj.redraw);
gui.add(controlObj, 'heightSegments', 0, 10).onChange(controlObj.redraw);
// add a material section, so we can switch between materials
gui.add(controlObj, 'appliedMaterial', {
  meshNormal: applyMeshNormalMaterial,
  meshStandard: applyMeshStandardMaterial,
}).onChange(controlObj.redraw)

gui.add(controlObj, 'castShadow').onChange(function(e) {controlObj.mesh.castShadow = e})
gui.add(controlObj, 'groundPlaneVisible').onChange(function(e) {plane.material.visible = e})

controlObj.redraw();

let step = 0;

const render = () => {
  state.update();
  orbitControl.update();

  step += 0.01;

  controlObj.mesh.rotation.y = step;
  controlObj.mesh.rotation.x = step;
  controlObj.mesh.rotation.z = step;

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(state.domElement);
  canvas.value.appendChild(renderer.domElement);

  render();
});

</script>
